package com.hb.gp.util.tools;

import java.nio.ByteBuffer;

import com.hb.gp.util.MsgGenUtil;
import com.hb.gp.util.cryptoapi.Des3Cipher;
import com.hb.gp.util.cryptoapi.DesCipher;

//import ademessage.*;
/**
 * <p>
 * Title: Appserver
 * </p>
 * <p>
 * Description: Appserver for GCA system
 * </p>
 * <p>
 * Copyright: Copyright (c) 2003
 * </p>
 * <p>
 * Company: Gemplus
 * </p>
 * 
 * @author Wang Chunmeng
 * @version 1.0
 */
public class MACGen {

	private int len = 0;

	// private byte[] src_data;

	public MACGen() {
		/*
		 * Provider sunJce = new com.sun.crypto.provider.SunJCE();
		 * Security.addProvider(sunJce);
		 */
	}

	/**
	 * This method performs TripleDES ENCRYPT
	 * 
	 * 
	 */
	public byte[] TDES(byte[] Key, byte[] data) {
		byte[] result = new byte[8];
		Des3Cipher des3 = new Des3Cipher(Key);
		des3.encrypt(data, 0, result, 0);
		return result;
	}

	/**
	 * This method performs TripleDES DECRYPT
	 * 
	 * 
	 */
	public byte[] TDES_D(byte[] Key, byte[] data) {
		byte[] result = new byte[9];
		Des3Cipher des3 = new Des3Cipher(Key);
		des3.decrypt(data, 0, result, 0);
		return result;
	}

	/**
	 * This method performs DES ENCRYPT
	 * 
	 * 
	 */
	public byte[] SDES(byte[] Key, byte[] data) {
		byte[] result = new byte[8];
		DesCipher des = new DesCipher(Key);
		des.encrypt(data, 0, result, 0);
		return result;
	}

	/**
	 * This method performs DES DECRYPT
	 * 
	 * 
	 */
	public byte[] SDES_D(byte[] Key, byte[] data) {
		byte[] result = new byte[9];
		DesCipher des = new DesCipher(Key);
		des.decrypt(data, 0, result, 0);
		return result;
	}

	/**
	 * Generate PBOC Derivation for CMCC OTA2
	 * 
	 * @return Fkey must be 16 bytes factor must be 8 bytes
	 */
	public byte[] pboc(byte[] Fkey, byte[] factor) {

		byte[] key = new byte[16];
		byte[] lkey = new byte[8];
		byte[] rkey = new byte[8];
		byte[] not_factor = new byte[8];

		for (int j = 0; j < 8; j++)
			not_factor[j] = (byte) ~factor[j];

		try {
			lkey = TDES(Fkey, factor);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		rkey = TDES(Fkey, not_factor);

		System.arraycopy(lkey, 0, key, 0, 8);
		System.arraycopy(rkey, 0, key, 8, 8);

		return key;
	}

	/**
	 * Generate session key using PBOC for CMCC OTA2
	 * 
	 * @return
	 */
	public byte[] genSK(byte[] Mkey, byte[] imsi, byte[] random) {
		byte[] sk = new byte[16];
		// kc=pboc(Mkey,imsi);
		// System.out.println( "-----kc------" + MsgGenUtil.buf2hex(kc));
		sk = pboc(Mkey, random);
		return sk;
	}

	public byte[] genSK(byte[] Mkey, byte[] random) {

		byte[] sk = new byte[16];

		sk = pboc(Mkey, random);
		return sk;
	}

	public static void main(String[] args) {

		// MACGen ma = new MACGen();
		// String src = "0101" + "000b" + "000000000000000000000000";
		// String key = "407DB4AEFC58BA7C490097BB60199204";
		// String pbCardKeyMacSk = DES.encryptTDS_CBC(MsgGenUtil.BCD2Bytes(key),
		// MsgGenUtil.BCD2Bytes(src));
		// System.out.println(pbCardKeyMacSk);
		// String aa = "74A719593279A9CC";

		// byte[] test =
		// ma.genMAC(MsgGenUtil.BCD2Bytes(pbCardKeyMacSk),MsgGenUtil.BCD2Bytes("84E400001A4F10A0000003330101060003030000000101"));
		// System.out.println(MsgGenUtil.buf2hex(test));

	}

	/**
	 * Generate MAC with parameter for cmcc ota2
	 * 
	 * @return
	 */
	public byte[] genMAC(byte[] sk, byte[] src, byte[] iv, boolean flag) {

		byte[] resMAC = null;
		String strMAC = "";

		// 软算法
		byte[] MAC = new byte[8];
		byte[] kc = new byte[16];
		byte[] mackey = new byte[8];
		System.arraycopy(sk, 0, kc, 0, 16);
		System.arraycopy(kc, 0, mackey, 0, 8);
		System.arraycopy(mackey, 0, kc, 8, 8);

		if (flag) {
			MAC = TDES(kc, iv);
		}

		byte[] padSrc = padSpace(src);
		int curLen = padSrc.length;
		int tmp = 0;
		for (int i = 0; i < curLen / 8 - 1; i++) {
			for (int j = 0; j < 8; j++)
				MAC[j] = (byte) (MAC[j] ^ padSrc[i * 8 + j]);
			MAC = TDES(kc, MAC);
			tmp = i + 1;
		}

		for (int j = 0; j < 8; j++)
			MAC[j] = (byte) (MAC[j] ^ padSrc[tmp * 8 + j]);
		strMAC = DES.encrypt(sk, MAC);

		// 加密机
		// EncryptService service=new EncryptService();
		// strMAC=service.encryptUKMac("000", MsgGenUtil.buf2hex(sk),
		// MsgGenUtil.buf2hex(src), MsgGenUtil.buf2hex(iv));

		resMAC = MsgGenUtil.BCD2Bytes(strMAC);
		return resMAC;
	}

	public byte[] gen4ByteMAC(byte[] sk, byte[] src, byte[] iv)
			throws Exception {
		if (sk.length != 8 && sk.length != 16)
			throw new Exception("Invalid key length. Key length must be 8.");

		// 软算法
		byte[] MAC = new byte[8];
		System.arraycopy(iv, 0, MAC, 0, 8);
		byte[] sdeskey = new byte[8];
		System.arraycopy(sk, 0, sdeskey, 0, 8);
		byte[] padSrc = padSpace(src);
		int curLen = padSrc.length;
		int tmp = 0;
		for (int i = 0; i < curLen / 8 - 1; i++) {
			for (int j = 0; j < 8; j++)
				MAC[j] = (byte) (MAC[j] ^ padSrc[i * 8 + j]);
			// System.out.println("MAC:" + MsgGenUtil.buf2hex(MAC));
			// System.out.println("KEY:" + MsgGenUtil.buf2hex(sk));
			MAC = SDES(sdeskey, MAC);
			// ;DES.encryptDES_CBC(MAC, sk);
			// System.out.println("EncMAC:" + MsgGenUtil.buf2hex(MAC));
			tmp = i + 1;
		}

		for (int j = 0; j < 8; j++)
			MAC[j] = (byte) (MAC[j] ^ padSrc[tmp * 8 + j]);
		if (sk.length == 8)
			MAC = SDES(sk, MAC);
		else
			MAC = TDES(sk, MAC);
		byte[] mac4 = new byte[4];
		System.arraycopy(MAC, 0, mac4, 0, 4);
		return mac4;

	}

	public String encryptMAC(byte[] sk, byte[] iv) {

		byte[] resMAC = null;
		byte[] kc = new byte[16];
		byte[] mackey = new byte[8];
		System.arraycopy(sk, 0, kc, 0, 16);
		System.arraycopy(kc, 0, mackey, 0, 8);
		System.arraycopy(mackey, 0, kc, 8, 8);

		return MsgGenUtil.buf2hex(TDES(kc, resMAC));
	}

	/**
	 * Pad space
	 * 
	 * @return
	 */
	private byte[] padSpace(byte[] src) {
		this.len = src.length;
		int padLen = 8 - len % 8;
		ByteBuffer bf = ByteBuffer.allocate(src.length + padLen);
		bf.put(src);

		bf.put((byte) 0x80);
		for (int i = 1; i < padLen; i++) {
			bf.put((byte) 0x00);
		}
		this.len = this.len + padLen;

		return bf.array();
	}

	/**
	 * Get kindex for cmcc ota2
	 * 
	 * @return
	 */
	public int getkindex(byte[] random) {
		int kindex = (random[3] & 0x07) % 5;
		return kindex;
	}

	public int getkindex3(byte[] random) {
		int kindex = (random[3] & 0x03) % 3;
		return kindex;
	}

}
